import json
import re
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed

import inquirer
from lxml import etree as et
import requests
from inquirer.themes import GreenPassion

baseHeaders = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
    'Content-Type': 'application/json',
}


def request(url: str, data: dict, retry: int = 3):
    times = 0
    while times < retry:
        try:
            res = requests.post(url, json=data, headers=baseHeaders)
            return res
        except requests.exceptions.ReadTimeout:
            times = times + 1
        except requests.exceptions.ConnectionError:
            return None
    return None


def get_item(pels, el):
    vel = el.find('./{*}version')
    if vel is None:
        return None
    group_id = el.find('./{*}groupId').text
    artifact_id = el.find('./{*}artifactId').text
    version = vel.text
    m = re.match('\\${(.+)}', version)
    mel = None
    if m is not None:
        if pels is None:
            return None
        mel = list(filter(lambda x: re.sub('\\{.+}', '', x.tag) == m.group(1), pels))
        if len(mel) == 0:
            return None
        version = mel[0].text
    url = 'https://central.sonatype.com/api/internal/browse/components'
    data = {"filter": [], "searchTerm": f"g:{group_id} a:{artifact_id}", "size": 10}
    res = request(url, data)
    if res is None or res.status_code < 200 or res.status_code > 299:
        return None
    data = json.loads(res.text)
    components = data['components']
    mls = list(filter(lambda x: x['namespace'] == group_id and x['name'] == artifact_id, components))
    if len(mls) == 0:
        return None
    new_version = mls[0]["latestVersionInfo"]["version"]
    if version == new_version:
        return None
    return {
        'g': group_id,
        'a': artifact_id,
        'version': version,
        'newVersion': new_version,
        'el': (vel if mel is None else mel[0]),
    }


def check_pom(pom: str):
    try:
        tree = et.parse(pom)
    except et.ParseError:
        print('错误：无法使用XML解析该的文件')
        return
    pels = tree.findall('./{*}properties/*')
    dels = tree.findall('.//{*}dependencies/{*}dependency')
    if dels is None:
        return

    ups = []
    total = len(dels)
    with ThreadPoolExecutor(max_workers=5) as executor:
        all_tasks = [executor.submit(get_item, pels, el) for el in dels]
        num = 0
        for task in as_completed(all_tasks):
            num = num + 1
            print("\r", end="")
            print("检查更新: [" + "#" * num + '-' * (total - num) + '] {}% {}/{}'.format(round(num / total * 100, 1), num, total), end="")
            sys.stdout.flush()
            result = task.result()
            if result is not None:
                ups.append(result)
        print('\n')

    if len(ups) > 0:
        print('检查完成，共有', len(ups), '项更新')
        msgs = list(map(lambda x: (f'{x["g"]}:{x["a"]}'.ljust(52, '-') + f'[{x["version"]} --> {x["newVersion"]}]', x), ups))
        questions = [
            inquirer.Checkbox('reps', message="请选择需要更新的项目", choices=msgs, )
        ]
        answers = inquirer.prompt(questions, theme=GreenPassion())
        if answers is not None and len(answers['reps']) > 0:
            for a in answers['reps']:
                a['el'].text = a['newVersion']
            tree.write(pom, encoding='utf-8')
            print('更新完成，本次更新', len(answers['reps']), '个项目')
        else:
            print('未选择任何更新，退出')
    else:
        print('没有可以更新的项目')


if __name__ == '__main__':
    if len(sys.argv) > 1:
        check_pom(sys.argv[1])
